package netty.demo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @author cyt
 * @create 2020-11-05 11:22
 */
public class GroupServer {
    /**
     * 服务端添加一个 selector 和 channel 用于网络IO 操作，
     * 在构造方法中对 选择去和通道进行初始化， 为通道注册选择器，设置关注事件。
     */
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT = 6667;

    public GroupServer() {
        try {
            selector = Selector.open();
            listenChannel = ServerSocketChannel.open();
            listenChannel.socket().bind(new InetSocketAddress(PORT));
            listenChannel.configureBlocking(false);
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void listen() {
        try {
            while (true) {
                /**
                 * 得到当前selector 管理通道中有事件发生的通道
                 */
                int count = selector.select();
                if (count > 0) {
                    /**
                     * 得到有事件发生通道的迭代器，然后卡开始遍历迭代器中的每一个元素
                     * 判断这个元素的事件， 进行处理。
                     */
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        /**
                         * 如果这是一个连接的事件， 就连接， 并且得到这个连接通道然后设置为
                         * 非阻塞， 随后注册到selector中， 设置为关注读事件。
                         */
                        if (key.isAcceptable()) {
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println(sc.getRemoteAddress() + "上线 - 》》》");
                        }
                        /**
                         * 如果这个事件是一个读事件， 那就调用读的方法来处理它
                         */
                        if (key.isReadable()) {
                            readData(key);
                        }
                        /**
                         * 啥也不是， 那就直接删除
                         */
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待-》》》");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }

    private void readData(SelectionKey key) {
        SocketChannel channel = null;
        try {
            /**
             * 反向得到这个通道， 并且开辟一个内存块， 把通道中的数据写入到
             * 内存块中， 并且把读取到的数据 发生给其他的客户端。
             */
            channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = channel.read(buffer);
            if (count > 0) {
                String msg = new String(buffer.array());
                System.out.println("from  client :" + msg);
                sendInfoToOtherClients(msg, channel);
            }
        } catch (Exception e) {
            try {
                System.out.println(channel.getRemoteAddress() + "离线了- 》》》");
                key.cancel();
                channel.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }

        }
    }

    private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
        System.out.println("服务器转发消息中 - 》》》");
        /**
         * 开始遍历当前selector 监控的所有通道， 并且把当前的消息写入到其他的通道
         */
        for (SelectionKey key : selector.keys()) {
            Channel channel = key.channel();
            if (channel instanceof SocketChannel && channel != self) {
                SocketChannel dest = (SocketChannel) channel;
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        GroupServer groupServer = new GroupServer();
        groupServer.listen();
    }
}

